﻿using FlexFlow.Base.Interface;
using FlexFlow.Model;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace FlexFlow.DataProvider.MongoDB
{
    /// <summary>
    /// 表示MongoDB物理数据提供器
    /// </summary>
    /// <typeparam name="T">
    /// 该提供器绑定的物理数据类型
    /// </typeparam>
    public abstract class MongoDBPhysicalModelProvider<T> : IPhysicalEntityProvider<T> where T : PhysicalModel
    {
        protected string connectionString = "mongodb://localhost:27017";
        protected string databaseName = "FlexFlow";
        protected MongoClient mongo;
        protected IMongoDatabase mongoDatabase;

        /// <summary>
        /// 取得当前提供器使用的MongoCollection
        /// </summary>
        /// <returns>
        /// MongoCollection
        /// </returns>
        internal protected IMongoCollection<T> GetCollection()
        {
            IMongoCollection<T> col = mongoDatabase.GetCollection<T>((typeof(T)).Name);
            return col;
        }
        /// <summary>
        /// 由派生类重写，创建类型为T的对象的实例
        /// </summary>
        /// <returns>
        /// 创建的对象实例
        /// </returns>
        public abstract T Create();
        /// <summary>
        /// 向MongoDB中加入指定模型实体
        /// </summary>
        /// <param name="model">
        /// 要加入的实体
        /// </param>
        /// <returns>
        /// 如果加入成功，则返回true，否则返回false
        /// </returns>
        public bool Add(T model)
        {
            IMongoCollection<T> col = mongoDatabase.GetCollection<T>((typeof(T)).Name);
            col.InsertOneAsync(model).Wait();
            return true;
        }
        /// <summary>
        /// 根据指定id从MongoDb中取出数据
        /// </summary>
        /// <param name="identity">
        /// 要取出的数据的唯一标识符
        /// </param>
        /// <returns>
        /// 若数据存在，则返回数据，否则返回null
        /// </returns>
        public T Get(Guid identity)
        {
            IMongoCollection<T> col = mongoDatabase.GetCollection<T>((typeof(T)).Name);
            FilterDefinition<T> filter = (FilterDefinition<T>)(m => m.Identity == identity);
            return col.FirstOrDefault(filter);
        }
        /// <summary>
        /// 更新指定模型
        /// </summary>
        /// <param name="model">
        /// 要更新的模型
        /// </param>
        /// <returns>
        /// 若模型更新成功，则返回true，若模型更新失败或模型不存在，则返回false
        /// </returns>
        public bool Update(T model)
        {
            IMongoCollection<T> col = mongoDatabase.GetCollection<T>((typeof(T)).Name);
            FilterDefinition<T> filter = (FilterDefinition<T>)(m => m._id.Equals(model._id));
            Task<ReplaceOneResult> replaceTask = col.ReplaceOneAsync(filter, model);
            replaceTask.Wait();
            return !(replaceTask.Result.ModifiedCount == 0 || (replaceTask.Result.MatchedCount != replaceTask.Result.ModifiedCount));
        }
        /// <summary>
        /// 从MongoDb中删除指定模型数据
        /// </summary>
        /// <param name="model">
        /// 要删除的模型
        /// </param>
        /// <returns>
        /// 若模型不存在或删除失败，则返回false，否则返回true
        /// </returns>
        public bool Delete(T model)
        {
            return Delete(model.Identity);
        }
        /// <summary>
        /// 删除具有指定ID的实体
        /// </summary>
        /// <param name="identity">
        /// 要删除的实体的ID
        /// </param>
        /// <returns>
        /// 一个表示是否删除成功的值
        /// </returns>
        public bool Delete(Guid identity)
        {
            IMongoCollection<T> col = mongoDatabase.GetCollection<T>((typeof(T)).Name);
            FilterDefinition<T> filter = (FilterDefinition<T>)(m => m.Identity == identity);
            Task<DeleteResult> delTask = col.DeleteOneAsync(filter);
            delTask.Wait();
            return delTask.Result.DeletedCount > 0;
        }
        /// <summary>
        /// 更新具有指定ID的实体
        /// </summary>
        /// <param name="Id">
        /// 要更新的实体ID
        /// </param>
        /// <param name="updef">
        /// 更新定义
        /// </param>
        /// <returns>
        /// 一个值，表示更新是否成功
        /// </returns>
        public bool Update(Guid Id, UpdateDefinition<T> updef)
        {
            IMongoCollection<T> col = GetCollection();
            FilterDefinition<T> filter = (FilterDefinition<T>)(m => m.Identity == Id);
            Task<T> findTask = col.FindOneAndUpdateAsync(filter, updef);
            findTask.Wait();
            return (findTask.Result != null);
        }
        /// <summary>
        /// 判断具有指定键的实体是否存在
        /// </summary>
        /// <param name="identity">
        /// 要判断的键
        /// </param>
        /// <returns>
        /// 一个表示是否存在的值
        /// </returns>
        public bool Exist(Guid identity)
        {
            IMongoCollection<T> col = GetCollection();
            FilterDefinition<T> filter = (FilterDefinition<T>)(m => m.Identity == identity);
            Task<long> countTsk = col.CountAsync(filter, new CountOptions()
            {
                Limit = 1
            });
            countTsk.Wait();
            return countTsk.Result == 1;
        }
        /// <summary>
        /// 通过指定的连接字符串与数据库名称初始化MongoDb物理实体提供器实例
        /// </summary>
        /// <param name="connectionString">
        /// 连接字符串
        /// </param>
        /// <param name="dbName">
        /// 数据库名称
        /// </param>
        public MongoDBPhysicalModelProvider(string connectionString = "mongodb://localhost:27017", string dbName = "FlexFlow")
        {
            this.connectionString = connectionString;
            this.databaseName = dbName;
            mongo = new MongoClient(connectionString);
            mongoDatabase = mongo.GetDatabase(databaseName);
        }
        /// <summary>
        /// 构造单一字段更新定义
        /// </summary>
        /// <typeparam name="TField">
        /// 字段类型
        /// </typeparam>
        /// <param name="field">
        /// 字段表达式
        /// </param>
        /// <param name="value">
        /// 字段新值
        /// </param>
        /// <param name="parDef">
        /// 链式定义的父级定义
        /// </param>
        /// <returns>
        /// 构造的定义
        /// </returns>
        public UpdateDefinition<T> BuildUpdateDef<TField>(Expression<Func<T, TField>> field, TField value,UpdateDefinition<T> parDef = null)
        {
            if(parDef==null)
            {
                return Builders<T>.Update.Set(field, value);
            }
            else
            {
                return parDef.Set(field, value);
            }
        }

        /// <summary>
        /// 更新指定模型的备注消息
        /// </summary>
        /// <param name="identity">
        /// 要更新备注消息的模型ID
        /// </param>
        /// <param name="tag">
        /// 要更新的备注消息
        /// </param>
        /// <returns>
        /// 一个值，表示更新是否成功
        /// </returns>
        public bool UpdateTag(Guid identity, string tag)
        {
            var col = GetCollection();
            var upDef = BuildUpdateDef(m => m.Tag, tag);
            return Update(identity, upDef);
        }
    }
}
